<?php
/**
 *
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2018/6/13 13:52
 * @copyright PanxSoft Inc.
 */

namespace App\Services\AliYun;

use App\Http\Controllers\Common\OSSController;
use DateTime;
use OSS\OssClient;

class OSS
{
    /**
     * @var OssClient
     */
    protected $client;

    protected $config = [
        'access_key_id'     => null,
        'access_key_secret' => null,
        'endpoint'          => null,
    ];

    /**
     * OSS constructor.
     *
     * @throws \OSS\Core\OssException
     */
    public function __construct()
    {
        $this->config['access_key_id'] = config('pkx.oss.access_key_id');
        $this->config['access_key_secret'] = config('pkx.oss.access_key_secret');
        $this->config['endpoint'] = config('pkx.oss.endpoint');
        $this->config['bucket'] = self::projectBucket();

        $this->client = new OssClient(
            $this->config['access_key_id'],
            $this->config['access_key_secret'],
            $this->config['endpoint']
        );
    }

    /**
     * @return OssClient
     *
     * @throws \OSS\Core\OssException
     */
    public static function getClient(): OssClient
    {
        return (new static())->client;
    }

    public static function projectBucket(): string
    {
        return config('pkx.oss.bucket');
    }

    /**
     * @param $method
     * @param $arguments
     *
     * @return mixed
     * @throws \OSS\Core\OssException
     */
    public static function __callStatic($method, $arguments)
    {
        return (new static)->$method(...$arguments);
    }

    public function __call($method, $arguments)
    {
        return $this->client->$method(...$arguments);
    }

    /**
     * @param $time
     *
     * @return string
     * @throws \Exception
     */
    private function timeToGMT_ISO8601($time) {
        $dtStr = date("c", $time);
        $dateTime = new DateTime($dtStr);
        $expiration = $dateTime->format(DateTime::ISO8601);
        $pos = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration."Z";
    }

    /**
     * @link https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1293.35c66e28fpFgpi
     *
     * @param string $dir_prefix - 用户上传文件时指定的前缀。
     * @param bool $use_callback - 前端上传完成后是否要求 aliyun 请求我服务器 callback 接口验证
     *
     * @return array
     * @throws \Exception
     */
    public function webUploaderSign($dir_prefix = 'misc/', $use_callback = true): array
    {
        $expire_time = time() + 30;

        $policy = $this->toBase64Json([
            //设置该 policy 超时时间. 即 policy 有效时间，将不能访问
            'expiration' => $this->timeToGMT_ISO8601($expire_time),
            'conditions' => [
                //最大文件大小.用户可以自己设置
                [
                    0 => 'content-length-range',
                    1 => 0,
                    2 => 1048576000 // 1G ?
                ],
                // 表示用户上传的数据，必须是以$dir_prefix开始，
                // 不然上传会失败，这一步不是必须项，
                // 只是为了安全起见，防止用户通过policy上传到别人的目录。
                [
                    0 => 'starts-with',
                    1 => '$key',
                    2 => $dir_prefix
                ]
            ]
        ]);

        $sign = [
            'accessid'  => $this->config['access_key_id'],
            'host'      => "https://" . $this->config['bucket'] . "." . $this->config['endpoint'],
            'policy'    => $policy,
            'expire'    => $expire_time,
            'dir'       => $dir_prefix,
            'signature' => base64_encode(hash_hmac(
                'sha1', $policy, $this->config['access_key_secret'], true
            )),
        ];

        if ($use_callback) {
            $sign['callback'] = $this->toBase64Json([
                // $callbackUrl为上传回调服务器的URL，请将下面的IP和Port配置为您自己的真实URL信息。
                'callbackUrl'      => route(OSSController::ROUTES['callback']),
                'callbackBody'     => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
                'callbackBodyType' => "application/x-www-form-urlencoded",
            ]);
        }

        return $sign;
    }

    private function toBase64Json(array $data): string
    {
        return base64_encode(json_encode($data));
    }
}